package com.xh3140.xwidget.app.chart;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;

import com.xh3140.xwidget.app.chart.scales.AxisScales;
import com.xh3140.xwidget.app.chart.scales.LinearAxisScales;
import com.xh3140.xwidget.app.utils.DimenUtils;

/**
 * 坐标系抽象类
 * 定义了基本属性和规范
 * 具体规范需要在子类中实现
 */
@SuppressWarnings("unused")
public abstract class AbstractCoordinatesView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
  // 坐标轴（手动）
  @ColorInt
  protected int mAxisColor = Color.BLACK;
  protected float mAxisThickness = 3f;

  // 网格线（手动）
  protected float mGridsBasicX = 100f;
  protected float mGridsBasicY = 100f;
  protected float mGridsBasicKeepX = 100f;
  protected float mGridsBasicKeepY = 100f;
  @ColorInt
  protected int mGridsColor = Color.GRAY;
  protected float mGridsThickness = 1f;

  // 坐标轴标签（手动）
  protected String mAxisLabelX = "x";
  protected String mAxisLabelY = "y";
  @ColorInt
  protected int mAxisLabelTextColor = Color.BLACK;
  protected float mAxisLabelTextSize = DimenUtils.sp2px(getContext(), 12f);
  protected Typeface mAxisLabelTypeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);

  // 刻度列表（手动）
  protected AxisScales mAxisScalesX = new LinearAxisScales(-100f, 1f, 200, 100, 0);
  protected AxisScales mAxisScalesY = new LinearAxisScales(-100f, 1f, 200, 100, 0);

  //  刻度列表标签（手动）
  @ColorInt
  protected int mAxisScalesLabelTextColor = Color.DKGRAY;
  protected float mAxisScalesLabelTextSize = DimenUtils.sp2px(getContext(), 9f);
  protected Typeface mAxisScalesLabelTypeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);

  // 线程绘图
  private Thread mThread;
  private Canvas mCanvas;
  private SurfaceHolder mHolder;
  private boolean mIsDrawing;
  @ColorInt
  private int mBackgroundColor = Color.WHITE;

  // 缩放事件
  private boolean mIsScaling = false;
  private final ScaleGestureDetector mScaleGestureDetector = new ScaleGestureDetector(getContext(),
          new ScaleGestureDetector.OnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
              return onScaleGesture(detector.getFocusX(), detector.getFocusY(), detector.getScaleFactor());
            }

            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
              mIsScaling = true;
              return true;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector detector) {
              mIsScaling = false;
            }
          });


  // 滑动事件
  private final GestureDetector mScrollGestureDetector = new GestureDetector(getContext(),
          new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
              if (mIsScaling) {
                return false;
              }
              return onScrollGesture(e1.getX(), e1.getY(), distanceX, distanceY);
            }
          });

  public AbstractCoordinatesView(Context context) {
    this(context, null);
  }

  public AbstractCoordinatesView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public AbstractCoordinatesView(Context context, AttributeSet attrs, int defStyleAttr) {
    this(context, attrs, defStyleAttr, 0);
  }

  public AbstractCoordinatesView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    mHolder = getHolder();
    setFocusable(true);
    setKeepScreenOn(true);
    setFocusableInTouchMode(true);
  }

  @Override
  protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    mHolder.addCallback(this);
  }

  /**
   * 将坐标系中的坐标转换为Canvas的实际坐标
   */
  protected abstract float getCanvasX(float x);

  protected abstract float getCanvasY(float y);

  protected PointF getCanvasPointF(PointF point) {
    return new PointF(getCanvasX(point.x), getCanvasY(point.y));
  }

  /**
   * 将Canvas的实际坐标转换为坐标系中的坐标
   */
  protected abstract float getCoordinatesX(float x);

  protected abstract float getCoordinatesY(float y);

  protected PointF getCoordinatesPointF(PointF point) {
    return new PointF(getCoordinatesX(point.x), getCoordinatesY(point.y));
  }

  /**
   * 缩放手势处理,控制坐标系的缩放
   *
   * @param focusX  焦点的X坐标
   * @param focusY  焦点的Y坐标
   * @param factory 缩放因子，小于1时代表缩小，大于1时代表放大
   * @return Boolean 是否进行了事件处理
   */
  protected abstract boolean onScaleGesture(float focusX, float focusY, float factory);

  /**
   * 滑动手势处理,控制坐标系的上下左右滑动
   *
   * @param startX    起点的X坐标
   * @param startY    起点的Y坐标
   * @param distanceX 滑动距离的X轴投影，等于起点的X坐标 - 当前触摸点的X坐标
   * @param distanceY 滑动距离的Y轴投影，等于起点的Y坐标 - 当前触摸点的Y坐标
   * @return Boolean 是否进行了事件处理
   */
  protected abstract boolean onScrollGesture(float startX, float startY, float distanceX, float distanceY);


  /**
   * 触摸事件，包含缩放事件和滑动事件
   * 应优先处理缩放手势，然后再处理滑动手势
   * 缩放手势为双触点手势，滑动手势为单触点手势，两者冲突
   * 在处理缩放手势时屏蔽滑动手势来防止冲突
   */
  @Override
  public boolean onTouchEvent(MotionEvent event) {
    mScaleGestureDetector.onTouchEvent(event);
    mScrollGestureDetector.onTouchEvent(event);
    return true;
  }

  @Override
  public void surfaceCreated(@NonNull SurfaceHolder holder) {
    mIsDrawing = true;
    mThread = new Thread(this);
    mThread.start();
  }

  @Override
  public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
    // do nothing
  }

  @Override
  public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
    mIsDrawing = false;
    try {
      mThread.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    mThread = null;
  }

  /**
   * 设置绘图背景颜色
   */
  @Override
  public void setBackgroundColor(int color) {
    if (mBackgroundColor != color) {
      mBackgroundColor = color;
      invalidate();
    }
  }

  /**
   * 线程绘图，绘制图形（代替onDraw方法）
   */
  protected abstract void onThreadDraw(Canvas canvas);

  /**
   * 绘图线程
   */
  @Override
  public void run() {
    while (mIsDrawing) {
      try {
        mCanvas = mHolder.lockCanvas();
        if (mCanvas != null) {
          // 开始绘制，获取的Canvas保存了之前绘制的痕迹
          mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
          // 绘制背景颜色
          if (mBackgroundColor != Color.TRANSPARENT) {
            mCanvas.drawColor(mBackgroundColor);
          }
          onThreadDraw(mCanvas);
        }
      } finally {
        // 绘制结束，提交绘制的Canvas
        if (mCanvas != null) {
          mHolder.unlockCanvasAndPost(mCanvas);
        }
      }
    }
  }
}
